(PYTHON) Day - 16 Collections(1)

Reference

  • 문제 출처 - HackerRank
  • 파이썬 연습 - Practice - Python

개인적인 생각과 상상으로 작성한 내용들이 포함되어 있습니다
문제를 풀고 Discussion Tab을 참고하며 코드 스타일을 개선하려고 노력하고자 합니다


HackerRank


Collections

기본개념

Collections Module
Collections 모듈은 dict, list, set, tuple과 같은 파이썬의 기본 내장 컨테이너들을 확장한 특수 컨테이너들을 가진다.

namedtuple()named fields를 가지는 서브클래스 튜플을 만드는 factory 함수
deque양끝에서 빠르게 append(추가)와 pop(삭제) 할 수 있는 list-like 컨테이너
ChainMap여러 매핑의 단일 뷰를 만드는 dict-like 클래스
Counter해시 가능한 객체의 요소 개수를 세는 데 사용하는 dict 서브클래스
OrderedDict항목이 추가된 순서를 기억하는 dict 서브클래스
defaultdict결측값에 기본 값을 자동으로 지정해주는 dict 서브클래스
UserDict더 쉬운 dict 서브 클래싱을 위해 dict 객체를 감싸는 wrapper
UserList더 쉬운 list 서브 클래싱을 위해 list 객체를 감싸는 wrapper
UserString더 쉬운 string 서브 클래싱을 위해 string 객체를 감싸는 wrapper

namedtuple()

tuple의 immutable한 성질을 띄면서 Dictionary Key로 접근할 수 있는 장점들을 가지고 있다.


> > > # 기본 예
> > >
> > > Point = namedtuple('Point', ['x', 'y'])
> > > p = Point(11, y=22) # 위치와 키워드 인자로 인스턴스를 만듭니다
> > > p[0] + p[1] # 일반 튜플 (11, 22) 처럼 인덱싱할 수 있습니다
> > > 33
> > > x, y = p # 일반 튜플처럼 언팩합니다
> > > x, y
> > > (11, 22)
> > > p.x + p.y # 필드는 이름으로도 액세스할 수 있습니다
> > > 33
> > > p # name=value 스타일의 가독성 있는 **repr**
> > > Point(x=11, y=22)
  • tuple, namedtuple, dict 비교

# runtime.py written in PyCharm

from time import time
from math import sqrt

# 일반적인 튜플 사용

pt1 = (1.0, 5.0)
pt2 = (2.5, 1.5)

start1= time()
line_leng1 = sqrt((pt2[0] - pt1[0]) ** 2 + (pt2[1] - pt1[1]) ** 2)
end1 = time()
print('tuple: '.rjust(15), round((end1-start1)\* 10\*\*3, 6))

###################################################################

# 네임드 튜플 사용

from collections import namedtuple

Point = namedtuple('Point', 'x y')
pnt1 = Point(1.0, 5.0)
pnt2 = Point(2.5, 1.5)

start2= time()
line_leng2 = sqrt((pnt2.x - pnt1.x) ** 2 + (pnt2.y - pnt1.y) ** 2)
end2 = time()
print('namedTuple: '.rjust(15), round((end2-start2)\* 10\*\*3, 6))

###################################################################

# 딕셔너리 사용

pdt1 = {'x': 1.0, 'y': 5.0}
pdt2 = {'x': 2.5, 'y': 1.5}

start3= time()
line_leng3 = sqrt((pdt2['x'] - pdt1['x']) ** 2 + (pdt2['y'] - pdt1['y']) ** 2)
end3 = time()
print('dict: '.rjust(15), round((end3-start3)\* 10\*\*3, 6))
tuple: 0.068188
namedTuple: 0.010014
dict: 0.004053

deque()

double-ended queue 의 줄임말로, 앞과 뒤에서 즉, 양방향에서 데이터를 처리할 수 있는 queue형 자료구조를 의미한다.
더하기 : 추가(append, appendleft), 확장(extend, extendleft), 삽입(insert)
빼기 : 비우기(clear), 제거(remove), 반환 및 제거(pop)
조작 : 뒤집기(reverse), 회전(rotate), 복사(copy)
확인 : 요소 수(count), 위치(index), 최대크기(maxlen)


# runtime.py written in PyCharm

from collections import deque

deq = deque(['a', 'b', 'c'])

deq.append('d')
print('append -> d: ', deq)
deq.appendleft('z')
print('append <- z: ', deq)
deq.extend('ef')
print('extend -> ef: ', deq) # list의 extend 결과와 조금 다른 것을 주의!!
deq.extendleft('xy')
print('extend <- xy: ', deq)

deql = deq.copy()

print('deque.pop() ->', end=' ')
while True:
try:
print(deq.pop(), end=' ')
except IndexError:
break

print()
print('deque.popleft() <-', end=' ')
while True:
try:
print(deql.popleft(), end=' ')
except IndexError:
break
append -> d: deque(['a', 'b', 'c', 'd'])
append <- z: deque(['z', 'a', 'b', 'c', 'd'])
extend -> ef: deque(['z', 'a', 'b', 'c', 'd', 'e', 'f'])
extend <- xy: deque(['y', 'x', 'z', 'a', 'b', 'c', 'd', 'e', 'f'])
deque.pop() -> f e d c b a z x y
deque.popleft() <- y x z a b c d e f

ChainMap()

파이썬 3.3v 부터 기능을 지원한다.
사실 기본 내장함수에서 dict.update() 를 통해 Chain of Dictionaries를 구현할 수 있기에 굳이 사용하지 않는 함수이다.
하지만 ChainMap() 함수를 사용함으로 코드 작성의 편의성과 가독성이 높아지는 장점을 무시 할 수는 없다.

a chain of mappings


# runtime.py written in PyCharm

from collections import ChainMap
from time import time

# ChainMap 사용

start = time()
toys = {'Blocks': 30, 'Monopoly': 20}
computers = {'iMac': 1000, 'Chromebook': 800, 'PC': 400}
clothing = {'Jeans': 40, 'T-Shirt': 10}
inventory_chain = ChainMap(toys, computers, clothing)
end = time()
run = end - start

print(round(run \* 10\*\*2, 5))
##########################################################

# dict.update() 사용

start1 = time()
inventory = dict()
inventory.update(toys)
inventory.update(computers)
inventory.update(clothing)
end1 = time()
run1 = end1 - start1

print(round(run1 \* 10\*\*2, 5))
##########################################################

# 내용 확인

print(inventory_chain if inventory_chain == inventory else 'Not same!')
0.00088
0.00062
ChainMap({'Blocks': 30, 'Monopoly': 20}, {'iMac': 1000, 'Chromebook': 800, 'PC': 400}, {'Jeans': 40, 'T-Shirt': 10})

Counter()

빈도수를 확인할 때 많이 사용된다


> > > # 목록에 있는 단어의 빈도를 집계
> > >
> > > cnt = Counter()
> > > for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
> > > ... cnt[word] += 1
> > > cnt
> > > Counter({'blue': 3, 'red': 2, 'green': 1})

> > > # 햄릿에서 가장 흔한 단어를 찾음
> > >
> > > import re
> > > words = re.findall(r'\w+', open('hamlet.txt').read().lower())
> > > Counter(words).most_common(10)
> > > [('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
> > > ('you', 554), ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]
> > >

OrderedDict()

딕셔너리 순서를 재배치하는데 사용된다.
popitem(last=True) : last가 참이면 LIFO 순서로, 거짓이면 FIFO 순서로 (key, value) 값을 반환한다
move_to_end(key, last=True) : key를 last가 참이면 오른쪽 끝으로, 거짓이면 왼쪽 끝으로 보낸다


> > > d.move_to_end('b')
> > > ''.join(d.keys())
> > > 'acdeb'
> > > d.move_to_end('b', last=False)
> > > ''.join(d.keys())
> > > 'bacde'
> > >